<?php
class Characteristic{

	public function __construct($id, $denomination, $category, $weight, $isEssential, $nature, $thresholdValue, $dimension){
		// $nature can be Boolean(1), Fuzzy(2), Numeric(3)
		$this->id = $id;
		$this->denomination = $denomination;
		$this->category = $category;
		$this->weight = $weight;
		$this->isEssential = $isEssential;
		$this->thresholdValue = $thresholdValue;
		$this->dimension = $dimension;
		$this->options = $this->getOptions($nature);
		$this->threshold = $this->getThreshold($nature);
	}

	public static function getWeightForDimension($dimensionId){
		switch ($dimensionId){
			case 1:
			case 3:
				// plausibilidad + adecuacion
				return 8;
			case 2:
				// justificacion
				return 3;
			case 4:
				// exito
				return 5;
		}
	}

	public function __toString(){
		return "Id: ".$this->id." Denomination: '".$this->denomination."' Category: ".$this->category." Threshold: ".$this->thresholdValue." Weight: ".$this->weight." Essential: ".$this->isEssential;
	}

	private function getOptions($nature){
		switch ($nature){
			case 1:
				return Options::booleanOptions();
			case 2:
				return Options::fuzzyOptions();
			case 3:
				return Options::numericOptions();
		}
	}

	private function getThreshold($nature){
		if (!($this->isEssential)){
			return new NullThreshold();
		}
		switch ($nature){
			case 1:		
				if ($this->weight > 0){

					return new PositiveBooleanThreshold($this->id);
				}
				return new NegativeBooleanThreshold($this->id);
			case 2:
				if ($this->weight > 0){
					return new PositiveFuzzyThreshold($this->id, $this->thresholdValue, $this->options);
				}
				return new NegativeFuzzyThreshold($this->id, $this->thresholdValue, $this->options);
			case 3:
				return new NumericThreshold($this->id, $this->thresholdValue);
		}
	}
}


class Options{
	public static function booleanOptions(){
		return array("si", "no");
	}

	public static function fuzzyOptions(){
		return array("muy poco o nada", "poco", "regular", "mucho", "muchisimo o todo");
	}

	public static function numericOptions(){
		return range(1, 10);
	}
}

class FuzzySet{
	public static function create($weight, $nature, $value){
		switch ($nature){
			case 1:
				if ($weight < 0){
					$value = 1 - $value;
				}
				if ($value == 0){
					return FuzzySet::yes();
				}
				return FuzzySet::no();
			case 2:
				if ($weight < 0){
					$value = 4 - $value;
				}
				switch ($value){
					case 0:
						return FuzzySet::veryLittleOrNone();
					case 1:
						return FuzzySet::aLittle();
					case 2:
						return FuzzySet::regular();
					case 3:
						return FuzzySet::much();
					case 4:
						return FuzzySet::veryMuchOrEverything();
				}
			case 3:
				if ($weight < 0){
					$value = 10 - $value;
				}
				return FuzzySet::number($value);
		}
	}

	private static function number($value){
		return new FuzzySet($value, $value, $value, $value);
	}

	private static function yes(){
		return new FuzzySet(10, 10, 10, 10);
	}

	public static function no(){
		return new FuzzySet(0, 0, 0, 0);
	}

	private static function veryLittleOrNone(){
		return new FuzzySet(0, 0, 1.2, 2.2);
	}

	private static function aLittle(){
		return new FuzzySet(1.2, 2.2, 3.4, 4.4);
	}

	private static function regular(){
		return new FuzzySet(3.4, 4.4, 5.6, 6.6);
	}

	private static function much(){
		return new FuzzySet(5.6, 6.6, 7.8, 8.8);
	}

	private static function veryMuchOrEverything(){
		return new FuzzySet(7.8, 8.8, 10, 10);
	}

	public function add($other){
		return new FuzzySet($this->lowMin + $other->lowMin, $this->lowMax + $other->lowMax, $this->highMax + $other->highMax,	$this->highMin + $other->highMin);
		$this->lowMin += $other->lowMin;	
		$this->lowMax += $other->lowMax;
		$this->highMax += $other->highMax;
		$this->highMin += $other->highMin;
	}

	public function divide($number){
		return new FuzzySet($this->lowMin/$number, $this->lowMax/$number, $this->highMax /$number, $this->highMin/$number);
	}

	public function multiply($number){
		return new FuzzySet($this->lowMin*$number, $this->lowMax*$number, $this->highMax *$number, $this->highMin*$number);
	}

	public function __toString(){
		return number_format($this->lowMin,2).";".number_format($this->lowMax,2).";".number_format($this->highMax,2).";".number_format($this->highMin,2);
	}

	private function __construct($lowMin, $lowMax, $highMax, $highMin){
		$this->lowMin = $lowMin;
		$this->lowMax = $lowMax;
		$this->highMax = $highMax;
		$this->highMin = $highMin;
	}
} 

class NullThreshold{
	public function causesError($chosenValue){
		return FALSE;
	}

	public function getError($chosenValue){
		return null;
	}
}

class PositiveBooleanThreshold{
	private $questionNumber;

	public function __construct($id){
		$this->questionNumber = $id;
	}

	public function causesError($chosenValue){
		return $chosenValue == 1;
	}

	public function getError($chosenValue){
		return "Respondio no a la pregunta ".$this->questionNumber.". La respuesta deberia ser si.";
	}

	public function __toString(){
		return "PositiveBooleanThreshold QuestionNumber: ".$this->questionNumber.".";
	}
}

class NegativeBooleanThreshold{
	private $questionNumber;

	public function __construct($id){
		$this->questionNumber = $id;
	}

	public function causesError($chosenValue){
		return $chosenValue == 0;
	}

	public function getError($chosenValue){
		return "Respondio si a la pregunta ".$this->questionNumber.". La respuesta deberia ser no.";
	}
}

class NegativeFuzzyThreshold{
	private $thresholdValue;
	private $questionNumber;
	private $options;

	public function __construct($id, $thresholdValue, $options){
		$this->thresholdValue = $thresholdValue;
		$this->questionNumber = $id;
		$this->options = $options;
	}

	public function causesError($chosenValue){
		return ($chosenValue + 1) > $this->thresholdValue;
	}

	public function getError($chosenValue){
		return "Respondio '".$this->options[$chosenValue]."' a la pregunta ".$this->questionNumber.". Como maximo debe ser ". $this->options[$this->thresholdValue];
	}
}

class PositiveFuzzyThreshold{
	private $thresholdValue;
	private $questionNumber;
	private $options;

	public function __construct($id, $thresholdValue, $options){
		$this->thresholdValue = $thresholdValue;
		$this->questionNumber = $id;
		$this->options = $options;
	}

	public function causesError($chosenValue){
		return ($chosenValue + 1) < $this->thresholdValue;
	}

	public function getError($chosenValue){
		return "Respondio '".$this->options[$chosenValue]."' a la pregunta ".$this->questionNumber.". Como minimo debe ser ". $this->options[$this->thresholdValue];
	}
}

class NumericThreshold{
	private $thresholdValue;
	private $questionNumber;

	public function __construct($questionNumber, $threshold){
		$this->thresholdValue = $threshold;
		$this->questionNumber = $questionNumber;
	}

	public function causesError($chosenValue){
		return ($chosenValue+1) < ($this->thresholdValue);
	}

	public function getError($chosenValue){
		return "Respondio ".($chosenValue+1)." a la pregunta ".$this->questionNumber.". Como minimo debe ser ". $this->thresholdValue;
	}
}

?>
